package com.catchman.controller.common;

import com.catchman.annotation.OperationLog;
import com.catchman.constant.Codes;
import com.catchman.entity.SysUser;
import com.catchman.vo.ResponseModel;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.UUID;

/**
 * 此类中的所有接口均是公开的，无需任何权限，配置 shiro 时需将其中所有接口均配置为可匿名访问
 * 登录接口以及相应错误接口
 * @author catchman
 */
@Api(tags = {"登录、认证接口"})
@RestController
@RequestMapping("/auth")
public class AuthController {
    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    /**
     * shiro.loginUrl 映射到这里，我在这里直接抛出异常交给GlobalExceptionHandler来统一返回json信息，
     * 您也可以在这里直接返回json，不过这样子就跟GlobalExceptionHandler中返回的json重复了。
     */
    @ApiOperation(value = "没有认证的401错误")
    @RequestMapping("/page/401")
    public ResponseModel page401() {
        throw new UnauthenticatedException();
    }

    /**
     * shiro.unauthorizedUrl 映射到这里。由于约定了url方式只做鉴权控制，不做权限访问控制，
     * 也就是说在ShiroConfig中如果没有做roles[js],perms[mvn:install]这样的权限访问控制配置的话，是不会跳转到这里的。
     */
    @ApiOperation(value = "没有权限的403错误")
    @RequestMapping("/page/403")
    public ResponseModel page403() {
        throw new UnauthorizedException();
    }

    /**
     * 登录成功跳转到这里，直接返回json。但是实际情况是在login方法中登录成功后返回json了。
     *
     * @return
     */
    @RequestMapping("/page/index")
    public ResponseModel pageIndex() {
        return new ResponseModel("index", true, 1, "index page", null);
    }

    /**
     * 登录接口
     */
    @ApiOperation(value = " 登录接口")
    @OperationLog("用户登录")
    @PostMapping("/login")
    public ResponseModel login(@RequestParam String uname, @RequestParam String pwd) {

        String oper = "user login";
        logger.info("{}, body: {}", oper, uname + "&" + pwd);

        if (StringUtils.isEmpty(uname)) {
            return ResponseModel.fail(oper, "用户名不能为空");
        }
        if (StringUtils.isEmpty(pwd)) {
            return ResponseModel.fail(oper, "密码不能为空");
        }


        Subject currentUser = SecurityUtils.getSubject();
        try {
            //登录
            currentUser.login(new UsernamePasswordToken(uname, pwd));
            // 从session 取出用户信息
            SysUser user = (SysUser) currentUser.getPrincipal();
            if (user == null) {
                throw new AuthenticationException();
            }
            logger.info("user login: {}, sessionId: {}", user.getUname(), currentUser.getSession().getId());
            //返回登录用户的信息给前台，含用户的所有角色和权限
            return ResponseModel.succ(oper)
                    .data("token", UUID.randomUUID().toString())
                    .data("uid", user.getUid())
                    .data("nick", user.getNick())
                    .data("roles", user.getRoles())
                    .data("perms", user.getPerms());

        } catch (UnknownAccountException uae) {
            logger.warn("用户帐号不正确");
            return ResponseModel.fail(oper, "用户帐号或密码不正确");

        } catch (IncorrectCredentialsException ice) {
            logger.warn("用户密码不正确");
            return ResponseModel.fail(oper, "用户帐号或密码不正确");

        } catch (LockedAccountException lae) {
            logger.warn("用户帐号被锁定");
            return ResponseModel.fail(oper, "用户帐号被锁定不可用");

        } catch (AuthenticationException ae) {
            logger.warn("登录出错");
            return ResponseModel.fail(oper, "登录失败：" + ae.getMessage());
        }
    }

    /**
     * 登出
     */
    @ApiOperation(value = "登出接口")
    @OperationLog("用户登出")
    @PostMapping("/logout")
    public ResponseModel logout() {
        String oper = "user logout";
        logger.info("{}", oper);
        SecurityUtils.getSubject().logout();
        return new ResponseModel(oper);
    }

    /**
     * 获取当前登陆用户信息
     */
    @ApiOperation(value = "获取当前登陆用户信息")
    @GetMapping("/info")
    public ResponseModel info() {
        String oper = "get user info";

        Subject subject = SecurityUtils.getSubject();

        Serializable sessionId = subject.getSession().getId();
        logger.info("{}, sessionId: {}", oper, sessionId);

        //从session取出用户信息
        SysUser user = (SysUser) subject.getPrincipal();
        if (user == null) {
            //告知前台，登录失效
            return new ResponseModel(oper, false, Codes.SESSION_TIMEOUT, "登录已失效", null);
        } else {
            //返回登录用户的信息给前台，含用户的所有角色和权限
            return ResponseModel.succ(oper)
                    .data("name", user.getUname())
                    .data("nick", user.getNick())
                    .data("avator", "")
                    .data("roles", user.getRoles())
                    .data("perms", user.getPerms());
        }


    }

}
